/* Copyright © Worklight Ltd. 2006-2011.  All rights reserved. */

package com.worklight.androidgap.plugin;

import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;

import android.app.Activity;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import com.google.android.c2dm.C2DMessaging;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import com.phonegap.api.PluginResult.Status;
import com.worklight.androidgap.WLAndroidUtils;
import com.worklight.androidgap.WLDroidGap;
import com.worklight.androidgap.push.C2DMErrorCode;
import com.worklight.androidgap.push.C2DMReceiver;

public class Push extends Plugin {
	private static final String WL_CLIENT_PUSH_ONMESSAGE = "WL.Client.Push.__onmessage";
	private static final String TOKEN = "token";
	private static final String DISPATCH = "dispatch";
	private static final String SUBSCRIBE = "subscribe";
	private static final String FROM_NOTIFICATION_BAR = "notificationBar";
	
	private String messageCallback = null;
	private ArrayList<C2DMReceiver.Message> pending = new ArrayList<C2DMReceiver.Message>();
	private String subscribeCallbackId = null;
	private String deviceToken = null;
	
	@Override
	public void setContext(PhonegapActivity ctx) {
		unregisterReceivers();
		super.setContext(ctx);
		if (ctx != null) {
			ctx.registerReceiver(onMessage, new IntentFilter(C2DMReceiver.C2DM_MESSAGE));
			if (C2DMReceiver.C2DM_NOTIFICATION.equals(ctx.getIntent().getAction())) {
				WLAndroidUtils.debug("Activity started from message notification");
				ctx.getIntent().putExtra(FROM_NOTIFICATION_BAR, true);
				onMessage.onReceive(ctx, ctx.getIntent());
			}
		}
		cancelAllNotification();
		dispatchPending();
	}

	@Override
	public void onResume (boolean multitasking) {
		super.onResume(multitasking);
		cancelAllNotification();
	}
	
	private void cancelAllNotification () {
		NotificationManager notificationManager = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
		notificationManager.cancelAll();
	}
	
	private void unregisterReceivers() {
		if (ctx != null) {
			try { 
				ctx.unregisterReceiver(onRegister); 
			} catch (Exception e) {
				WLAndroidUtils.warning("unregisterReceivers:" + e.getLocalizedMessage());
			}
			try { 
				ctx.unregisterReceiver(onMessage); 
			} catch (Exception e) {
				WLAndroidUtils.warning("unregisterReceivers:" + e.getLocalizedMessage());
			}
		}
	}

	@Override
	public PluginResult execute(String action, JSONArray args, String callbackId) {
		// can execute it only from sdk 8
		if (Integer.valueOf(android.os.Build.VERSION.SDK) < 8) {
			String errorMsg = "The current Android verion " + android.os.Build.VERSION.RELEASE + " not allowed to work with push.";
			WLAndroidUtils.error(errorMsg);
			return new PluginResult(Status.ERROR, errorMsg);
		}
		if (SUBSCRIBE.equals(action)) {
			return subscribe(args, callbackId);
		} else if (DISPATCH.equals(action)) {
			return dispatch(args);
		} else if (TOKEN.equals(action)) {
			return new PluginResult(Status.OK, this.deviceToken);
		}else { 
			return new PluginResult(Status.INVALID_ACTION);
		}
	}
	
	@Override
	public boolean isSynch(String action) {
		return !(SUBSCRIBE.equals(action));
	}

	@Override
	public void onDestroy() {
		unregisterReceivers();
		super.onDestroy();
	}

	@SuppressWarnings("unused")
	private PluginResult subscribe(JSONArray args, String callbackId) {
		WLAndroidUtils.debug("Registering at the C2DM server.");
		subscribeCallbackId = callbackId;
		// just in case subscribe() was called multiple times by the javascript script:
		try { 
			ctx.unregisterReceiver(onRegister); 
		} catch (Exception e) {
			//Nothing to do here
		}
		ctx.registerReceiver(onRegister, new IntentFilter(C2DMReceiver.C2DM_REGISTERED));
		ctx.registerReceiver(onRegisterError, new IntentFilter(C2DMReceiver.C2DM_ERROR));
		C2DMessaging.register(ctx, WLDroidGap.getWLConfig().getC2DMSender());
		PluginResult pluginResult = new PluginResult(Status.NO_RESULT);
		pluginResult.setKeepCallback(true);
		return pluginResult;
	}

	/**
	 * Recive the onRegister callback from the reciver
	 */
	private BroadcastReceiver onRegister = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			if (subscribeCallbackId != null) {
				WLAndroidUtils.debug("Notifying javascript on successful registration");
				Push.this.deviceToken = intent.getStringExtra(C2DMReceiver.C2DM_EXTRA_REGISTRATION_ID);
				success(new PluginResult(Status.OK, Push.this.deviceToken), subscribeCallbackId);
				subscribeCallbackId = null;
			} else { 
				WLAndroidUtils.debug("Not notifying javascript on successful registration because no subscribeCallbackId was set");
			}
			ctx.unregisterReceiver(onRegister);
			ctx.unregisterReceiver(onRegisterError);
		}
	};

	/**
	 * Recive the onRegister callback from the reciver
	 */
	private BroadcastReceiver onRegisterError = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String error = intent.getStringExtra(C2DMReceiver.C2DM_EXTRA_ERROR_ID);
			C2DMErrorCode c2dmError;
			try {
				c2dmError = C2DMErrorCode.valueOf(error);
			} catch (Exception e){
				c2dmError = C2DMErrorCode.UNEXPECTED;
				WLAndroidUtils.error("Push Service: additional information for unexpected error: " + error);
			}
			if (subscribeCallbackId != null) {
				WLAndroidUtils.error(c2dmError.getDescription() + "Notifying javascript on unsuccessful registration - ");
				error(new PluginResult(Status.ERROR, c2dmError.getDescription()), subscribeCallbackId);
				subscribeCallbackId = null;
			} else { 
				WLAndroidUtils.error(c2dmError.getDescription() + ".  Not notifying javascript on unsuccessful registration because no subscribeCallbackId was set.");
			}
			ctx.unregisterReceiver(onRegister);
			ctx.unregisterReceiver(onRegisterError);
		}
	};

	/**
	 * Recive the onMessage callback from the reciver
	 */
	private BroadcastReceiver onMessage = new BroadcastReceiver() {
		
		@Override
		public void onReceive(Context context, Intent intent) {
			WLAndroidUtils.debug("Queuing message for dispatch to javascript");
			synchronized (pending) {
				pending.add((C2DMReceiver.Message)intent.getParcelableExtra(C2DMReceiver.C2DM_EXTRA_MESSAGE));
			}
			dispatchPending();
			boolean isFromNotificationBar = intent.getBooleanExtra(FROM_NOTIFICATION_BAR, false);
			if (!isFromNotificationBar) {
				setResultCode(Activity.RESULT_OK);
			}
		}
	};

	private PluginResult dispatch(JSONArray args) {
		String method = null;
		try {
			method = args.getString(0);
		} catch (JSONException e) {
			WLAndroidUtils.warning("Push.dsipatch(): Unable to get method name from args");
		}
		if (method != null) {
			method = method.trim();
		}
		if ("".equals(method)){ 
			method = null;
		}
		if (messageCallback == null && method != null || messageCallback != null && !(messageCallback.equals(method))){
			messageCallback = method;
			WLAndroidUtils.debug("Javascript script requests " + (method == null ? "to stop dispatching" : "dispatching to " + method));
		}
		dispatchPending();
		return new PluginResult(Status.OK);
	}
	
	private void dispatchPending() {
		if (ctx.hasWindowFocus()) {
			messageCallback = WL_CLIENT_PUSH_ONMESSAGE;
		}
		if (ctx == null || messageCallback == null) {
			return;
		}
		StringBuilder jsCallSB = new StringBuilder();
		while (true) {
			C2DMReceiver.Message message = null;
			synchronized (pending) {
				if (pending.size() > 0) message = pending.remove(0);
			}
			if (message == null) {
				break;
			}
			WLAndroidUtils.debug("Dispatching to javascript " + message.toString());
			jsCallSB.setLength(0);
			jsCallSB.append(messageCallback);
			jsCallSB.append('(');
			jsCallSB.append(message.getProps().toString());
			jsCallSB.append(',');
			jsCallSB.append(message.getPayload().toString());
			jsCallSB.append(");");
			sendJavascript(jsCallSB.toString());
		}
	}
}
